/******************************************************************************
 * FILE: omp_hello.c
 * DESCRIPTION:
 *   OpenMP Example - Hello World - C/C++ Version
 *   In this simple example, the master thread forks a parallel region.
 *   All threads in the team obtain their unique thread number and print it.
 *   The master thread only prints the total number of threads.  Two OpenMP
 *   library routines are used to obtain the number of threads and each
 *   thread's number.
 * AUTHOR: Blaise Barney  5/99
 * LAST REVISED: 04/06/05
 ******************************************************************************/
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <iostream>
using namespace std;

static int N;
static int NBPOS;
static int HOLEPOS;


typedef struct {	
	_Bool *gameHoles;
	int	nombreDeBille;
}GAMETABLE;


int calculateNbpos(int n)
{
	int nbPos = ((n+1)*n)/2;
	return nbPos;
}

void initN ()
{
	cout << "Entrer le nombre de lignes de votre solitaire :";
	cin >> N;
	NBPOS = calculateNbpos(N);
	cout <<"Nombre de ligne du plateau :"<< N<<endl;
	cout <<"Nombre d'emplacement de la plateau :"<<NBPOS<<endl;
}

void initHolePos ()
{
	cout << "Entrer la position du trou au départ (1 Ã  n) :";
	cin >> HOLEPOS;

}




GAMETABLE* initGameTable()
{
	GAMETABLE *gametable;
	if ((gametable = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
		if ((gametable->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
			for (int i = 0; i < NBPOS; i++) {
				if (i == HOLEPOS-1) {
					gametable->gameHoles[i] = 0x0;
				}else {
					gametable->gameHoles[i] = 0x1;
				}				
			}
		}
	}	
	gametable->nombreDeBille = NBPOS-1;
	return gametable;
}




void printGameTable(GAMETABLE* gametable)
{
	printf("Game table");
	for (int j=0; j<NBPOS; j++) 
	{
		printf(" %d -", gametable->gameHoles[j]);
	}
	printf("\n");
}




void findMoves2(GAMETABLE* gametable)
{
	int countElemPerLine=0;
	int currentLine = 1;
	
	
	//printf("Nbre de bille %d \n",gametable->nombreDeBille);
	if (gametable->nombreDeBille ==1) {
		//printGameTable(gametable);
	}
	//printGameTable(gametable);

	
	
	// Test toutes les positions du Plateau pour trouver tous les mouvements possibles et exÃ©cuter les plateaux ayant une bille en moins (utilise un for sÃ©quentiel pour parcourir en profondeur)
	for (int i=0;  i< (NBPOS); i++) 
	{
		countElemPerLine +=1;
		
		if (gametable->gameHoles[i] == 0x0) // Si il y a un trou on essaie tous les mouvements pour le combler
		{ 			
				#pragma omp parallel num_threads(2)  shared(i,currentLine,countElemPerLine)
				{
					
					
					#pragma omp sections 
					{
						
						
						#pragma omp section 
						{
							//Obtenir le prochain plateau avec un mouvement horizontal depuis la gauche
							if (gametable->gameHoles[i-1] == 0x1 && gametable->gameHoles[i-2] == 0x1 && ((countElemPerLine - 2) > 0)) 
							{
								GAMETABLE *gametableMhg;
								if ((gametableMhg = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMhg->gameHoles = (_Bool *)malloc(NBPOS * sizeof(_Bool))) != NULL) {
										gametableMhg->nombreDeBille = gametable->nombreDeBille - 1;

									
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
										
											for (j=0; j<NBPOS; j++) 
											{
												gametableMhg->gameHoles[j] = gametable->gameHoles[j];
											}									
										}
									
										gametableMhg->gameHoles[i] = 0x1;
										gametableMhg->gameHoles[i-1] = 0x0;
										gametableMhg->gameHoles[i-2] = 0x0;
										findMoves2(gametableMhg);
									}
								}
							}
						}	
						
						
						
					
						#pragma omp section
						{
							//Obtenir le prochain plateau avec un mouvement horizontal depuis la doite
							if (gametable->gameHoles[i+1] == 0x1 && gametable->gameHoles[i+2] == 0x1 && (countElemPerLine + 2) < (currentLine + 1)) 
							{
								GAMETABLE *gametableMhd;
								if ((gametableMhd = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMhd->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMhd->nombreDeBille = gametable->nombreDeBille - 1;

										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMhd->gameHoles[j] = gametable->gameHoles[j];	
											}										
										}

										gametableMhd->gameHoles[i] = 0x1;
										gametableMhd->gameHoles[i+1] = 0x0;
										gametableMhd->gameHoles[i+2] = 0x0;
										findMoves2(gametableMhd);
									}	
								}
							}	
						}
						
						
						
					
						#pragma omp section
						{
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin haut gauche
							if (gametable->gameHoles[i-currentLine] == 0x1 && gametable->gameHoles[((i-currentLine)-currentLine+1)] == 0x1 && (countElemPerLine - 2) > 0) 
							{
								GAMETABLE *gametableMohg;
								if ((gametableMohg = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMohg->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMohg->nombreDeBille = gametable->nombreDeBille - 1;
										
										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMohg->gameHoles[j] = gametable->gameHoles[j];
											}								
										}
										
										gametableMohg->gameHoles[i] = 0x1;
										gametableMohg->gameHoles[(i-currentLine)] = 0x0;
										gametableMohg->gameHoles[((i-currentLine)-currentLine+1)] = 0x0;
										findMoves2(gametableMohg);
									}
								}
							}	
						}
						
						

					
						#pragma omp section
						{	
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin bas droite
							if (gametable->gameHoles[i+currentLine+1] == 0x1 && gametable->gameHoles[(i+currentLine+1)+(currentLine+2)] == 0x1 && currentLine < (N-1)) 
							{
								GAMETABLE *gametableMobd;
								if ((gametableMobd = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMobd->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMobd->nombreDeBille = gametable->nombreDeBille - 1;
										

										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMobd->gameHoles[j] = gametable->gameHoles[j];
											}											
										}
										
										gametableMobd->gameHoles[i] = 0x1;
										gametableMobd->gameHoles[(i+currentLine+1)] = 0x0;
										gametableMobd->gameHoles[(i+currentLine+1)+(currentLine+2)] = 0x0;
										findMoves2(gametableMobd);
									}
								}
							}
						}
						

						
					
						#pragma omp section
						{				
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin bas gauche
							if (gametable->gameHoles[i+currentLine] == 0x1 && gametable->gameHoles[(i+currentLine)+(currentLine+1)] == 0x1 && currentLine < (N-1)) 
							{
								GAMETABLE *gametableMobg;
								if ((gametableMobg = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMobg->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMobg->nombreDeBille = gametable->nombreDeBille - 1;
										
										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMobg->gameHoles[j] = gametable->gameHoles[j];
											}								
										}
										
										gametableMobg->gameHoles[i] = 0x1;
										gametableMobg->gameHoles[(i+currentLine)] = 0x0;
										gametableMobg->gameHoles[(i+currentLine)+(currentLine+1)] = 0x0;
										findMoves2(gametableMobg);
									}
								}
							}	
						}
					
						
						
						
						#pragma omp section
						{
							//Obtenir le prochain plateau avec un mouvement oblique depuis le coin haut droite
							if (gametable->gameHoles[(i-currentLine+1)] == 0x1 && gametable->gameHoles[((i-currentLine+1)-currentLine+2)] == 0x1 && (countElemPerLine + 2) < (currentLine + 1)) 
							{
								GAMETABLE *gametableMohd;
								if ((gametableMohd = (GAMETABLE *)malloc(sizeof(GAMETABLE))) != NULL) {
									if ((gametableMohd->gameHoles = (_Bool *)malloc(NBPOS*sizeof(_Bool))) != NULL) {
										gametableMohd->nombreDeBille = gametable->nombreDeBille - 1;
										
										
										#pragma omp parallel num_threads(6) 
										{
											int j;
											
											#pragma omp for
											
											for (j=0; j<NBPOS; j++) 
											{
												gametableMohd->gameHoles[j] = gametable->gameHoles[j];
											}										
										}	
										
										gametableMohd->gameHoles[i] = 0x1;
										gametableMohd->gameHoles[(i-currentLine+1)] = 0x0;
										gametableMohd->gameHoles[((i-currentLine+1)-currentLine+2)] = 0x0;
										findMoves2(gametableMohd);
									}
								}
							}
							
						}
					}
				}
			}
		
			if (countElemPerLine == currentLine) {
			countElemPerLine = 0;
			currentLine += 1;
		}
	}
	
	// TO DO
	// if (!gametable->IsInDispertionTable){ free(gameTable)}
	free(gametable);
	
}	
		
















void findMoves1(GAMETABLE* gametable)
{
	int d=0;
	int g=0;
	for (int m=1; m<(N+1); m++) {
		d=d+m;
		g=d;
		for (int i=1; i<(N+1); i++) {
			g=g+i-1;
			if (gametable->gameHoles[g] == 0x1 && gametable->gameHoles[g+i] == 0x1 && gametable->gameHoles[g+2*i+1] == 0x0 ){
				
				// a move has been found
				//TO DO crÃ©er un nouveau plateau avec la bille en moins et le nombre de billes -1 
				// et soit l'ajouter Ã  la liste finale car reste qu une bille, soit l'ajouter Ã  la liste des tache et l
			}
		}
	}
}








void testParallele()
{
	int nthreads, tid;
	/* Fork a team of threads giving them their own copies of variables */
#pragma omp parallel  private(nthreads, tid)
	{
		
		/* Obtain thread number */
		tid = omp_get_thread_num();
		printf("Hello World from thread = %d\n", tid);
		
		/* Only master thread does this */
		if (tid == 0) 
		{
			nthreads = omp_get_num_threads();
			printf("Number of threads = %d\n", nthreads);
		}
	}  /* All threads join master thread and disband */
}

	








int main (int argc, char *argv[]) 
{

	
	
	initN();
	initHolePos();
	//N = 5;
	//calculateNbpos(N);
	//HOLEPOS =5;
	
	findMoves2(initGameTable());
	
	

	
}





